package com.cavy.platform.server.service;

import com.cavy.platform.server.CavyConfig;
import com.cavy.platform.server.domain.Agent;
import com.cavy.platform.server.domain.AgentOnline;
import com.cavy.platform.server.domain.AgentStatus;
import com.cavy.platform.server.domain.message.ErrorMessage;
import com.cavy.platform.server.domain.message.Message;
import com.cavy.platform.server.domain.message.MessageHandler;
import com.cavy.platform.server.repository.AgentRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.websocket.Session;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 唐旭
 * @Date 2018/7/24
 */
@Service
@Slf4j
public class AgentSessionService {
    public static final int FIXED_RATE = 2000;
    @Autowired
    private List<MessageHandler> handlerList = new ArrayList<>();
    private ConcurrentHashMap<String, AgentOnline> onlineAgent = new ConcurrentHashMap<>();

    @Autowired
    private AgentRepository repository;
    @Autowired
    private CavyConfig config;

    @Scheduled(fixedRate = FIXED_RATE)
    public void heartbeatCheck() {
        log.debug("开始检查agent健康状态...");
        Collection<AgentOnline> values = onlineAgent.values();
        List<AgentOnline> removeList = new ArrayList<>();
        for (AgentOnline value : values) {
            if (value.isOffLine(config.getAgent())) {
                removeList.add(value);
            }
        }
        for (AgentOnline agentOnline : removeList) {
            agentOnline.offLine();
        }
        if (!removeList.isEmpty()) {
            values.removeAll(removeList);
            log.warn("移除不健康节点{}", getRemoveListString(removeList));
        }
        log.debug("健康检查完成");
    }

    private String getRemoveListString(List<AgentOnline> removeList) {
        StringBuilder builder = new StringBuilder();
        for (AgentOnline item : removeList) {
            builder.append(item.getInfo().getHostname()).append(",");
        }
        if (builder.length() > 0) {
            builder.deleteCharAt(builder.length() - 1);
        }
        return builder.toString();
    }

    public void addAgent(Session session, String agentId) {
        Agent agent = repository.findByHostname(agentId);
        if (agent == null) {
            return;
        }
        onlineAgent.put(agentId, new AgentOnline(agent, session));
    }

    public void removeAgent(Session session, String agentId) throws IOException {
        onlineAgent.remove(agentId);
    }

    public void handlerMessage(Session session, String agentId, String message) throws IOException {
        AgentOnline agent = onlineAgent.get(agentId);
        if (agent == null) {
            notFoundAgent(session, agentId);
            return;
        }
        MessageHandler handler = resolverHandler(message);
        if (handler == null) {
            notFoundHandler(session, message);
        } else {
            handler.handler(session, message, agent);
        }
    }

    private void notFoundHandler(Session session, String message) throws IOException {
        session.getBasicRemote().sendText(new ErrorMessage("没有对应的命令处理器").toJson());
    }

    private void notFoundAgent(Session session, String agentId) throws IOException {
        session.getBasicRemote().sendText(new ErrorMessage("未找到agent:" + agentId).toJson());
    }

    private MessageHandler resolverHandler(String message) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            JsonNode jsonNode = mapper.readTree(message);
            JsonNode eventName = jsonNode.get(Message.MESSAGE_TYPE_NODE_NAME);
            for (MessageHandler handler : handlerList) {
                if (handler.support(eventName.textValue())) {
                    return handler;
                }
            }
        } catch (IOException e) {
            log.error("无法读取消息,不支持的消息:{}", message);
            //此处不进行特殊处理,不支持的命令任然可以接收,但是不进行处理
            e.printStackTrace();
        }
        return null;
    }

    public Collection<AgentOnline> getOnlineAgentList() {
        return onlineAgent.values();
    }

    public void changeStatus(String hostName, AgentStatus status) {
        AgentOnline agentOnline = onlineAgent.get(hostName);
        if (agentOnline != null) {
            agentOnline.getInfo().setStatus(status);
        }
    }
}
